package io.github.guojiaxing1995.easyJmeter.common.jmeter;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import io.github.guojiaxing1995.easyJmeter.model.JFileDO;
import io.github.guojiaxing1995.easyJmeter.model.ReportDO;
import io.github.guojiaxing1995.easyJmeter.model.TaskDO;
import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Slf4j
public class JtlDataProcessor {
    
    private static final int BUFFER_SIZE = 8192;
    
    public ReportDO processJtlData(TaskDO taskDO, String jtlPath, JFileDO jFileDO) {
        log.info("开始直接处理JTL文件数据: {}", jtlPath);
        long startTime = System.currentTimeMillis();
        
        File jtlFile = new File(jtlPath);
        if (!jtlFile.exists()) {
            log.error("JTL文件不存在: {}", jtlPath);
            return null;
        }
        
        log.info("JTL文件大小: {}MB", jtlFile.length() / (1024 * 1024));
        
        try {
            // 初始化数据结构
            Map<String, List<JSONObject>> dashBoardData = new HashMap<>();
            
            // 处理统计数据
            log.info("开始处理统计数据...");
            processStatistics(jtlPath, dashBoardData);
            log.info("统计数据处理完成");
            
            // 创建ReportDO对象
            ReportDO reportDO = new ReportDO();
            reportDO.setTaskId(taskDO.getTaskId());
            reportDO.setDashBoardData(dashBoardData);
            reportDO.setFile(jFileDO);
            reportDO.setCaseId(taskDO.getJmeterCase());
            reportDO.setCreateTime(taskDO.getCreateTime());
            reportDO.setResult(taskDO.getResult());
            
            log.info("JTL数据处理完成，耗时: {}秒", (System.currentTimeMillis() - startTime) / 1000.0);
            return reportDO;
            
        } catch (Exception e) {
            log.error("处理JTL数据失败: {}", e.getMessage(), e);
            return null;
        }
    }
    
    private void processStatistics(String jtlPath, Map<String, List<JSONObject>> dashBoardData) throws IOException {
        // 使用ConcurrentHashMap存储统计数据
        Map<String, JtlStatistics> statisticsMap = new ConcurrentHashMap<>();
        statisticsMap.put("Total", new JtlStatistics()); // 添加总体统计
        
        // 读取JTL文件并处理数据
        long lineCount = 0;
        long startTime = System.currentTimeMillis();
        
        try (BufferedReader reader = new BufferedReader(new FileReader(jtlPath), BUFFER_SIZE)) {
            String headerLine = reader.readLine();
            if (headerLine == null) {
                throw new IOException("JTL文件为空");
            }
            
            // 解析标题行找到各字段的索引
            String[] headers = headerLine.split(",");
            int labelIndex = findColumnIndex(headers, "label");
            int elapsedIndex = findColumnIndex(headers, "elapsed");
            int successIndex = findColumnIndex(headers, "success");
            int timestampIndex = findColumnIndex(headers, "timeStamp", true);
            int responseCodeIndex = findColumnIndex(headers, "responseCode", true);
            int responseMessageIndex = findColumnIndex(headers, "responseMessage", true);
            int bytesIndex = findColumnIndex(headers, "bytes", true);
            int sentBytesIndex = findColumnIndex(headers, "sentBytes", true);
            
            String line;
            while ((line = reader.readLine()) != null) {
                lineCount++;
                if (lineCount % 100000 == 0) {
                    log.info("已处理 {} 行数据", lineCount);
                }
                
                try {
                    String[] values = line.split(",");
                    if (values.length <= Math.max(labelIndex, Math.max(elapsedIndex, successIndex))) {
                        continue;
                    }
                    
                    String label = values[labelIndex];
                    long elapsed = Long.parseLong(values[elapsedIndex]);
                    boolean success = Boolean.parseBoolean(values[successIndex]);
                    
                    // 获取可选字段
                    long timestamp = timestampIndex >= 0 && timestampIndex < values.length ? 
                        Long.parseLong(values[timestampIndex]) : System.currentTimeMillis();
                    
                    String responseCode = responseCodeIndex >= 0 && responseCodeIndex < values.length ? 
                        values[responseCodeIndex] : "";
                    
                    String responseMessage = responseMessageIndex >= 0 && responseMessageIndex < values.length ? 
                        values[responseMessageIndex] : "";
                    
                    long received = bytesIndex >= 0 && bytesIndex < values.length ? 
                        safeParseLong(values[bytesIndex], 0) : 0;
                    
                    long sent = sentBytesIndex >= 0 && sentBytesIndex < values.length ? 
                        safeParseLong(values[sentBytesIndex], 0) : 0;
                    
                    // 更新标签对应的统计信息
                    JtlStatistics labelStats = statisticsMap.computeIfAbsent(label, k -> new JtlStatistics());
                    labelStats.addSample(elapsed, success, timestamp, responseCode, responseMessage, received, sent);
                    
                    // 更新总体统计信息
                    JtlStatistics totalStats = statisticsMap.get("Total");
                    totalStats.addSample(elapsed, success, timestamp, responseCode, responseMessage, received, sent);
                    
                } catch (Exception e) {
                    log.warn("处理JTL行时出错: {}, 行: {}", e.getMessage(), line);
                }
            }
        }
        
        log.info("JTL文件统计处理完成，共 {} 行，用时: {}秒", 
                lineCount, (System.currentTimeMillis() - startTime) / 1000.0);
        
        // 生成统计表格数据
        List<JSONObject> statisticsTable = new ArrayList<>();
        
        // 先添加"Total"行
        JtlStatistics totalStats = statisticsMap.remove("Total");
        if (totalStats != null) {
            JSONObject totalRow = createStatisticsRow("Total", totalStats);
            statisticsTable.add(totalRow);
        }
        
        // 然后添加其他标签的统计行
        for (Map.Entry<String, JtlStatistics> entry : statisticsMap.entrySet()) {
            JSONObject row = createStatisticsRow(entry.getKey(), entry.getValue());
            statisticsTable.add(row);
        }
        
        dashBoardData.put("statisticsTable", statisticsTable);
        
        // 添加错误表格数据
        if (totalStats != null) {
            List<JSONObject> errorsTable = createErrorsTable(totalStats);
            if (!errorsTable.isEmpty()) {
                dashBoardData.put("errorsTable", errorsTable);
            }
        }
    }
    
    private JSONObject createStatisticsRow(String label, JtlStatistics stats) {
        JSONObject row = new JSONObject();
        row.put("label", label);
        row.put("samples", stats.getSampleCount());
        row.put("fail", stats.getErrorCount());
        row.put("error", formatPercentage(stats.getErrorRate()));
        row.put("average", formatDecimal(stats.getAverageResponseTime()));
        row.put("min", stats.getMinResponseTime());
        row.put("max", stats.getMaxResponseTime());
        row.put("median", formatDecimal(stats.getMedianResponseTime()));
        row.put("90th", formatDecimal(stats.getPercentile(90)));
        row.put("95th", formatDecimal(stats.getPercentile(95)));
        row.put("99th", formatDecimal(stats.getPercentile(99)));
        row.put("transactions", formatDecimal(stats.getTransactionsPerSecond()));
        row.put("received", formatDecimal(stats.getReceivedKBPerSecond()));
        row.put("Sent", formatDecimal(stats.getSentKBPerSecond()));
        return row;
    }
    
    private List<JSONObject> createErrorsTable(JtlStatistics stats) {
        List<JSONObject> errorsTable = new ArrayList<>();
        Map<String, Integer> errorCounts = stats.getErrorCounts();
        
        if (errorCounts.isEmpty()) {
            return errorsTable;
        }
        
        long totalErrors = stats.getErrorCount();
        long totalSamples = stats.getSampleCount();
        
        for (Map.Entry<String, Integer> entry : errorCounts.entrySet()) {
            JSONObject errorRow = new JSONObject();
            errorRow.put("type", entry.getKey());
            errorRow.put("number", entry.getValue());
            errorRow.put("currentPercent", formatPercentage((double) entry.getValue() / totalErrors * 100));
            errorRow.put("allPercent", formatPercentage((double) entry.getValue() / totalSamples * 100));
            errorsTable.add(errorRow);
        }
        
        return errorsTable;
    }
    
    private int findColumnIndex(String[] headers, String columnName) {
        return findColumnIndex(headers, columnName, false);
    }
    
    private int findColumnIndex(String[] headers, String columnName, boolean allowMissing) {
        for (int i = 0; i < headers.length; i++) {
            if (headers[i].equals(columnName)) {
                return i;
            }
        }
        if (allowMissing) {
            return -1;
        }
        throw new IllegalArgumentException("找不到列: " + columnName);
    }
    
    private long safeParseLong(String value, long defaultValue) {
        try {
            return Long.parseLong(value);
        } catch (NumberFormatException e) {
            return defaultValue;
        }
    }
    
    private double formatDecimal(double value) {
        return new BigDecimal(value).setScale(2, RoundingMode.HALF_UP).doubleValue();
    }
    
    private double formatPercentage(double value) {
        return new BigDecimal(value).setScale(2, RoundingMode.HALF_UP).doubleValue();
    }
    
    private static class JtlStatistics {
        private long sampleCount = 0;
        private long errorCount = 0;
        private long totalElapsedTime = 0;
        private long minResponseTime = Long.MAX_VALUE;
        private long maxResponseTime = Long.MIN_VALUE;
        private final List<Long> elapsedTimes = Collections.synchronizedList(new ArrayList<>());
        private long startTime = Long.MAX_VALUE;
        private long endTime = Long.MIN_VALUE;
        private long receivedBytes = 0;
        private long sentBytes = 0;
        private final Map<String, Integer> errorCounts = new ConcurrentHashMap<>();
        
        public synchronized void addSample(long elapsed, boolean success, long timestamp, 
                String responseCode, String responseMessage, long received, long sent) {
            sampleCount++;
            if (!success) {
                errorCount++;
                String errorKey = responseCode;
                if (responseMessage != null && !responseMessage.isEmpty()) {
                    errorKey += ": " + responseMessage;
                }
                errorCounts.merge(errorKey, 1, Integer::sum);
            }
            
            totalElapsedTime += elapsed;
            
            if (elapsed < minResponseTime) {
                minResponseTime = elapsed;
            }
            
            if (elapsed > maxResponseTime) {
                maxResponseTime = elapsed;
            }
            
            // 保持合理的样本大小来计算百分位数
            // 对于大数据集，只保留部分样本以避免内存溢出
            if (elapsedTimes.size() < 10000 || Math.random() < 0.1) {
                elapsedTimes.add(elapsed);
            }
            
            // 更新开始和结束时间
            if (timestamp < startTime) {
                startTime = timestamp;
            }
            
            if (timestamp > endTime) {
                endTime = timestamp;
            }
            
            receivedBytes += received;
            sentBytes += sent;
        }
        
        public long getSampleCount() {
            return sampleCount;
        }
        
        public long getErrorCount() {
            return errorCount;
        }
        
        public double getErrorRate() {
            return sampleCount == 0 ? 0 : (double) errorCount / sampleCount * 100;
        }
        
        public double getAverageResponseTime() {
            return sampleCount == 0 ? 0 : (double) totalElapsedTime / sampleCount;
        }
        
        public long getMinResponseTime() {
            return minResponseTime == Long.MAX_VALUE ? 0 : minResponseTime;
        }
        
        public long getMaxResponseTime() {
            return maxResponseTime == Long.MIN_VALUE ? 0 : maxResponseTime;
        }
        
        public double getMedianResponseTime() {
            return getPercentile(50);
        }
        
        public double getPercentile(int percentile) {
            if (elapsedTimes.isEmpty()) {
                return 0;
            }
            
            List<Long> sortedTimes = new ArrayList<>(elapsedTimes);
            Collections.sort(sortedTimes);
            
            int index = (int) Math.ceil(percentile / 100.0 * sortedTimes.size()) - 1;
            if (index < 0) index = 0;
            if (index >= sortedTimes.size()) index = sortedTimes.size() - 1;
            
            return sortedTimes.get(index);
        }
        
        public double getTransactionsPerSecond() {
            if (startTime == Long.MAX_VALUE || endTime == Long.MIN_VALUE || startTime == endTime) {
                return 0;
            }
            double durationSeconds = (endTime - startTime) / 1000.0;
            return durationSeconds <= 0 ? 0 : sampleCount / durationSeconds;
        }
        
        public double getReceivedKBPerSecond() {
            if (startTime == Long.MAX_VALUE || endTime == Long.MIN_VALUE || startTime == endTime) {
                return 0;
            }
            double durationSeconds = (endTime - startTime) / 1000.0;
            return durationSeconds <= 0 ? 0 : (receivedBytes / 1024.0) / durationSeconds;
        }
        
        public double getSentKBPerSecond() {
            if (startTime == Long.MAX_VALUE || endTime == Long.MIN_VALUE || startTime == endTime) {
                return 0;
            }
            double durationSeconds = (endTime - startTime) / 1000.0;
            return durationSeconds <= 0 ? 0 : (sentBytes / 1024.0) / durationSeconds;
        }
        
        public Map<String, Integer> getErrorCounts() {
            return errorCounts;
        }
    }
} 